iT邦幫忙

2025 iThome 鐵人賽

DAY 28
0
自我挑戰組

基於 Oracle 資料庫的醫院電子病歷系統設計與建置系列 第 28

Day28:前端設計 - 統整全部程式碼

  • 分享至 

  • xImage
  •  

大家好 ~ 今天是自我挑戰的第28天!今天要分享的是我所有的程式碼!
首先先介紹我的檔案內容文件:

oracle-test/
├── backend/
│   ├── middleware/
│   │   └── auth.js
│   ├── routes/
│   │   ├── auth.js
│   │   ├── patients.js
│   │   └── records.js
│   ├── database.js
│   ├── package-lock.json
│   ├── package.json
│   └── server.js
├── frontend/
│   ├── index.html ✅
│   ├── patients.html ✅
│   ├── patients.js ✅
│   ├── records.css ✅
│   ├── records.html ✅
│   ├── records.js ✅
│   ├── script.js ✅
│   └── style.css ✅
├── .env
├── server.js
├── test-oracle11g.js
└── test.js

index.html

<!-- 使用者打開網頁時第一個看到的頁面,讓醫護人員可以登入或註冊帳號 -->
<!DOCTYPE html>
<html lang="zh-Hant">
<!-- 定義網頁基本資訊(如編碼、viewport以適應不同裝置)、頁面標題(Login/Register) -->
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Login/Register</title>
  <!-- 引入style.css檔案,美化頁面的外觀 -->
  <link rel="stylesheet" href="style.css">
</head>
<body>
  <!-- 導覽列(包含多個<button>,有"Login"、"Patients"、"Records") -->  
  <nav class="navbar">
    <!-- class="active":表示目前停留在此頁面,CSS會有特別的樣式 -->
    <button class="active">Login</button>
    <button>Patients</button>
    <button>Records</button>
  </nav>
  <!-- 主區塊(使用Flexbox佈局將內容垂直置中) -->
  <div class="container">
    <!-- 顯示一個使用者頭像的圖示 -->
    <div class="avatar">
      <svg class="user-icon" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
        <path d="M12 12C14.2091 12 16 10.2091 16 8C16 5.79086 14.2091 4 12 4C9.79086 4 8 5.79086 8 8C8 10.2091 9.79086 12 12 12Z" fill="#666"/>
        <path d="M12 14C9.33 14 4 15.34 4 18V19C4 19.55 4.45 20 5 20H19C19.55 20 20 19.55 20 19V18C20 15.34 14.67 14 12 14Z" fill="#666"/>
      </svg>
    </div>
    
    <!-- 登入表單區塊 -->
    <div class="forms">
      <div class="form login">
        <!-- 表單標題 -->
        <h2>登入</h2>
        
        <label>使用者ID</label>
        <input type="text" id="loginId" placeholder="請輸入醫療人員ID">
        <!-- 用來顯示ID或密碼的錯誤訊息 -->
        <div class="error" id="loginIdError"></div>
        
        <label>使用者密碼</label>
        <input type="password" id="loginPwd" placeholder="請輸入密碼(需與ID相同)">
        <div class="error" id="loginPwdError"></div>
        
        <!-- 觸發登入邏輯的按鈕 -->
        <button id="loginBtn">登入</button>
        <div class="success-message" id="loginSuccess"></div>
        
        <!-- 點擊後會彈出註冊視窗的按鈕 -->
        <button id="showRegisterBtn" style="margin-top: 10px; background-color: #6c757d;">前往註冊</button>
      </div>
    </div>
  </div>

  <!-- 註冊彈出視窗 -->
  <div id="registerModal" class="modal">
    <div class="modal-content">
      <div class="form register">
        <h2>註冊</h2>
        
        <label>使用者ID</label>
        <input type="text" id="regId" placeholder="請輸入醫療人員ID">
        <div class="error" id="regIdError"></div>
        
        <label>使用者密碼</label>
        <input type="password" id="regPwd" placeholder="密碼將自動設置為您的ID" readonly>
        <div class="error" id="regPwdError"></div>
        
        <div class="modal-buttons">
          <button id="registerBtn">註冊</button>
          <button id="cancelRegisterBtn">取消</button>
        </div>
        <div class="success-message" id="registerSuccess"></div>
      </div>
    </div>
  </div>
  <!-- 引入script.js檔案 -->
  <script src="script.js"></script>
</body>
</html>

script.js

// 儲存了所有後端有效的醫療人員ID列表
const validMedicalIds = [
    "35102", "36215", "37488", "40756", "41839", "42922", 
    "45188", "48737", "49920", "51103", "52286", "53469", 
    "54652", "57018", "58101", "59284", "61650"
];

// 醫療人員角色對照表
const medicalRoles = {
    "36215": "doctor", "40756": "doctor", "45188": "doctor", 
    "49920": "doctor", "54652": "doctor", "59284": "doctor",
    "35102": "nurse", "37488": "nurse", "42922": "nurse", 
    "51103": "nurse", "53469": "nurse", "58101": "nurse",
    "41839": "pharmacist", "48737": "pharmacist", "57018": "pharmacist", "61650": "pharmacist",
    "52286": "therapist"
};

// 角色名稱對照表(將英文轉為中文名稱)
const roleNames = {
    "doctor": "醫師",
    "nurse": "護理師", 
    "pharmacist": "藥師",
    "therapist": "治療師"
};

// 從瀏覽器的localStorage中獲取已註冊的使用者資料
let users = JSON.parse(localStorage.getItem('medicalUsers')) || [];

// 事件監聽器
document.addEventListener('DOMContentLoaded', function() {
    
    // 表單驗證函數
    function validateForm(id, pwd, isLogin = false) {
        const errors = {
            id: '',
            pwd: ''
        };

        // 檢查使用者ID是否為空、是否存在於validMedicalIds(必須是有效的醫療人員ID)
        if (!id) {
            errors.id = '此處為必填!';
        } else if (!validMedicalIds.includes(id)) {
            errors.id = '無效的醫療人員ID!';
        }

        // 檢查密碼是否為空、是否存在於validMedicalIds(必須與ID完全相同)
        if (!pwd) {
            errors.pwd = '此處為必填!';
        } else if (pwd !== id) {
            errors.pwd = '密碼需與ID相同!';
        }
        // 返回一個包含錯誤訊息的物件
        return errors;
    }

    // 顯示錯誤消息和紅框
    function showErrors(errors, prefix) {
        // 清除所有錯誤樣式
        const idInput = document.getElementById(`${prefix}Id`);
        const pwdInput = document.getElementById(`${prefix}Pwd`);
        
        if (idInput) idInput.classList.remove('error-border');
        if (pwdInput) pwdInput.classList.remove('error-border');
        
        // 設置錯誤消息
        const idError = document.getElementById(`${prefix}IdError`);
        const pwdError = document.getElementById(`${prefix}PwdError`);
        
        if (idError) idError.textContent = errors.id || '';
        if (pwdError) pwdError.textContent = errors.pwd || '';
        
        // 添加紅框樣式
        if (errors.id && idInput) {
            idInput.classList.add('error-border');
        }
        if (errors.pwd && pwdInput) {
            pwdInput.classList.add('error-border');
        }
    }

    // 控制註冊彈窗和"尚未註冊"提示視窗的顯示
    function showNotRegisteredModal() {
        const existingModal = document.getElementById('notRegisteredModal');
        if (existingModal) {
            existingModal.remove();
        }
        
        const modal = document.createElement('div');
        modal.id = 'notRegisteredModal';
        modal.style.cssText = `
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0,0,0,0.5);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 1000;
        `;
        
        const modalContent = document.createElement('div');
        modalContent.style.cssText = `
            background: white;
            padding: 30px;
            border-radius: 10px;
            text-align: center;
            box-shadow: 0 4px 15px rgba(0,0,0,0.2);
            max-width: 400px;
            width: 90%;
            animation: modalAppear 0.3s ease-out;
        `;
        
        const messageElement = document.createElement('p');
        messageElement.textContent = '您尚未註冊,請先註冊再登入!';
        messageElement.style.cssText = `
            margin-bottom: 20px;
            font-size: 16px;
            color: #333;
            line-height: 1.5;
        `;
        
        const buttonContainer = document.createElement('div');
        buttonContainer.style.cssText = `
            display: flex;
            justify-content: center;
            gap: 10px;
        `;
        
        const confirmButton = document.createElement('button');
        confirmButton.textContent = '前往註冊';
        confirmButton.style.cssText = `
            padding: 10px 20px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        `;
        
        confirmButton.addEventListener('mouseenter', function() {
            this.style.backgroundColor = '#45a049';
        });
        
        confirmButton.addEventListener('mouseleave', function() {
            this.style.backgroundColor = '#4CAF50';
        });
        
        confirmButton.addEventListener('click', function() {
            modal.remove();
            showRegisterModal();
        });

        const cancelButton = document.createElement('button');
        cancelButton.textContent = '取消';
        cancelButton.style.cssText = `
            padding: 10px 20px;
            background-color: #6c757d;
            color: white;
            border: none;
            border-radius: 6px;
            cursor: pointer;
            font-size: 14px;
            transition: background-color 0.3s;
        `;
        
        cancelButton.addEventListener('click', function() {
            modal.remove();
        });
        
        buttonContainer.appendChild(confirmButton);
        buttonContainer.appendChild(cancelButton);
        modalContent.appendChild(messageElement);
        modalContent.appendChild(buttonContainer);
        modal.appendChild(modalContent);
        document.body.appendChild(modal);
    }

    // 顯示註冊視窗
    function showRegisterModal() {
        const modal = document.getElementById('registerModal');
        if (modal) {
            modal.style.display = 'flex';
            clearForm('reg');
        }
    }

    // 隱藏註冊視窗
    function hideRegisterModal() {
        const modal = document.getElementById('registerModal');
        if (modal) {
            modal.style.display = 'none';
        }
    }

    // 清除表單和錯誤樣式
    function clearForm(prefix) {
        const idInput = document.getElementById(`${prefix}Id`);
        const pwdInput = document.getElementById(`${prefix}Pwd`);
        
        if (idInput) idInput.value = '';
        if (pwdInput) pwdInput.value = '';
        
        if (idInput) idInput.classList.remove('error-border');
        if (pwdInput) pwdInput.classList.remove('error-border');
        
        showErrors({id: '', pwd: ''}, prefix);
    }

    // 檢查是否為未註冊用戶
    function checkIfIdNotRegistered(id) {
        return !users.some(u => u.id === id);
    }

    // 檢查登入憑證
    function checkLoginCredentials(id, pwd) {
        return users.some(u => u.id === id && u.pwd === pwd);
    }

    // 獲取使用者角色
    function getUserRole(id) {
        return medicalRoles[id] || null;
    }

    // 獲取角色名稱
    function getRoleName(role) {
        return roleNames[role] || '使用者';
    }

    // 登入成功後跳轉到 Patients 頁面
    function loginSuccess(id) {
        const role = getUserRole(id);
        const roleName = getRoleName(role);
        
        // 儲存使用者資訊到 localStorage
        const userInfo = {
            employeeId: id,
            role: role,
            roleName: roleName
        };
        
        localStorage.setItem('userInfo', JSON.stringify(userInfo));
        
        alert(`登入成功!歡迎 ${roleName}!`);
        clearForm('login');
        
        // 跳轉到 Patients 頁面
        window.location.href = 'patients.html';
    }

    // === 登入按鈕事件 ===
    const loginBtn = document.getElementById('loginBtn');
    if (loginBtn) {
        loginBtn.addEventListener('click', function() {
            const id = document.getElementById('loginId').value;
            const pwd = document.getElementById('loginPwd').value;
            
            const errors = validateForm(id, pwd, true);
            showErrors(errors, 'login');
            
            if (errors.id || errors.pwd) {
                return;
            }
            
            // 登入邏輯流程:
            // 1. 先檢查ID是否已註冊
            if (checkIfIdNotRegistered(id)) {
                showNotRegisteredModal();
                return;
            }
            
            // 2. 檢查密碼是否正確
            if (checkLoginCredentials(id, pwd)) {
                loginSuccess(id);
            } else {
                const loginErrors = {
                    id: '',
                    pwd: '密碼錯誤,請再試一次'
                };
                showErrors(loginErrors, 'login');
            }
        });
    }

    // === 註冊按鈕事件 ===
    const registerBtn = document.getElementById('registerBtn');
    if (registerBtn) {
        registerBtn.addEventListener('click', function() {
            const id = document.getElementById('regId').value;
            const pwd = id; // 密碼自動設置為ID
            document.getElementById('regPwd').value = pwd;
            
            const errors = validateForm(id, pwd, false);
            showErrors(errors, 'reg');
            
            if (!errors.id && !errors.pwd) {
                // 檢查ID是否已被使用
                const idExists = users.some(u => u.id === id);
                
                if (idExists) {
                    const duplicateErrors = {
                        id: 'ID已有人使用!請重新輸入!',
                        pwd: ''
                    };
                    showErrors(duplicateErrors, 'reg');
                    return;
                }
                
                // 如果都沒有錯誤,進行註冊
                users.push({ id, pwd });
                localStorage.setItem('medicalUsers', JSON.stringify(users));
                alert('註冊成功!');
                clearForm('reg');
                hideRegisterModal();
                
                // 註冊成功後自動填入登入表單
                document.getElementById('loginId').value = id;
            }
        });
    }

    // 顯示註冊視窗按鈕
    const showRegisterBtn = document.getElementById('showRegisterBtn');
    if (showRegisterBtn) {
        showRegisterBtn.addEventListener('click', function() {
            showRegisterModal();
        });
    }

    // 取消註冊按鈕
    const cancelRegisterBtn = document.getElementById('cancelRegisterBtn');
    if (cancelRegisterBtn) {
        cancelRegisterBtn.addEventListener('click', function() {
            hideRegisterModal();
        });
    }

    // 點擊視窗外部關閉
    const registerModal = document.getElementById('registerModal');
    if (registerModal) {
        registerModal.addEventListener('click', function(e) {
            if (e.target === this) {
                hideRegisterModal();
            }
        });
    }

    // 導覽按鈕
    const navButtons = document.querySelectorAll('.navbar button');
    if (navButtons.length > 0) {
        navButtons.forEach(button => {
            button.addEventListener('click', function() {
                document.querySelectorAll('.navbar button').forEach(btn => {
                    btn.classList.remove('active');
                });
                this.classList.add('active');
                
                const buttonText = this.textContent;
                switch(buttonText) {
                    case 'Login':
                        window.location.href = 'index.html';
                        break;
                    case 'Patients':
                        // 檢查是否已登入
                        const userInfo = localStorage.getItem('userInfo');
                        if (userInfo) {
                            window.location.href = 'patients.html';
                        } else {
                            alert('請先登入系統!');
                            window.location.href = 'index.html';
                        }
                        break;
                    case 'Records':
                        window.location.href = 'records.html';
                        break;
                    case 'Schedules':
                        window.location.href = 'schedules.html';
                        break;
                    case 'MedicalResourceUsage':
                        window.location.href = 'resources.html';
                        break;
                }
            });
        });
    }
    
    // 自動檢查登入狀態,如果已登入則跳轉到 Patients
    const userInfo = localStorage.getItem('userInfo');
    if (userInfo && window.location.pathname.includes('index.html')) {
        window.location.href = 'patients.html';
    }
});

style.css

/* 設定整個網站的背景顏色(淺藍色)和預設字體 */
body {
  font-family: Arial, sans-serif;
  margin: 0;
  background-color: #e6f4f7;
}

/* 導覽列樣式:包括背景色、置中對齊 */
.navbar {
  background-color: #a3d5e4;
  padding: 15px 10px;
  display: flex;
  justify-content: center;
  gap: 20px;
}

/* 設定導覽列按鈕的通用樣式 */
.navbar button {
   background: white; /* 按鈕為白色背景 */
  border: none;
  border-radius: 8px; /* 圓角效果 */
  padding: 10px 25px; /* 按鈕內邊距 */
  color: #333; /* 文字顏色 */
  font-size: 14px;
  font-weight: bold; /* 讓文字變粗體 */
  cursor: pointer;
  transition: all 0.2s ease-in-out;
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

/* 為當前頁面的按鈕設定不同樣式 */
.navbar button.active {
  color: rgb(0, 0, 0);
}

/* 主容器 */
.container {
  display: flex;
  flex-direction: column;
  align-items: center;
  margin-top: 40px;
}

/* 頭像 */
.avatar {
  width: 100px;
  height: 100px;
  border-radius: 50%;
  background-color: #ccc;
  margin-bottom: 30px;
}

/* 表單區塊 */
.forms {
  display: flex;
  gap: 50px;
}

.form {
  background: white;
  padding: 20px;
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  width: 250px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.form h2 {
  text-align: center;
}

.form input {
  padding: 8px;
  border: 1px solid #ccc;
  border-radius: 6px;
}

.form button {
  padding: 10px;
  border: none;
  background-color: #4CAF50;
  color: white;
  border-radius: 6px;
  cursor: pointer;
}

/* 滑鼠移過 */
.form button:hover {
  background-color: #3d8b40; /* 稍微深一點的綠色 */
}

/* 點下去 */
#loginBtn:active, #registerBtn:active {
  background-color: #3c6e3e; /* 更深的綠色 */
}

button {
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  background-color: #4CAF50; /* 原本顏色 */
  color: rgb(0, 0, 0);
  cursor: pointer;
  transition: background-color 0.2s ease; /* 平滑過渡 */
}

/* 滑鼠移過 */
button:hover {
  background-color: #4585d3; /* 稍微深一點 */
}

/* 點下去 */
button:active {
  background-color: #1b4d8a; /* 更深的顏色 */
}

.form .error {
  color: #e74c3c;  /* 紅色 */
  font-size: 14px;
  min-height: 20px;
  margin-top: -5px;
}

/* 錯誤邊框樣式 */
.form input.error-border {
  border-color: #e74c3c;
}

/* 彈出視窗的樣式 */
.modal {
  display: none;
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.5);
  z-index: 1000;
  justify-content: center;
  align-items: center;
}

.modal-content {
  background-color: white;
  border-radius: 10px;
  box-shadow: 0 4px 8px rgba(0,0,0,0.1);
  animation: modalAppear 0.3s ease-out;
}

/* 讓彈窗出現時更加流暢 */
@keyframes modalAppear {
  from {
    opacity: 0;
    transform: scale(0.9);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

.modal-buttons {
  display: flex;
  gap: 10px;
  margin-top: 10px;
}

.modal-buttons button {
  flex: 1;
}

#cancelRegisterBtn {
  background-color: #6c757d;
}

#cancelRegisterBtn:hover {
  background-color: #5a6268;
}

/* 病患頁面專用樣式 */
.patients-container {
  max-width: 1200px;
  margin: 0 auto;
  padding: 20px;
}

/* 提醒框樣式 */
.reminder-box {
  background: #fff3cd;
  border: 1px solid #ffeaa7;
  border-radius: 8px;
  padding: 15px;
  margin-bottom: 30px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
}

.reminder-icon {
  font-size: 24px;
}

.patient-actions-card {
  background: white;
  border-radius: 8px;
  padding: 24px;
  box-shadow: 0 2px 10px rgba(0,0,0,0.07);
  border: 1px solid #EAECEE;
  margin-bottom: 30px;
  display: flex;
  gap: 30px;
  align-items: flex-end; /* 讓內容底部對齊 */
}

.reminder-text {
  color: #856404;
  line-height: 1.5;
}

/* 搜尋和操作區 - 水平排列 */
.search-operations-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 30px;
  gap: 20px;
}

/* 搜尋容器 */
.search-container {
  display: flex;
  gap: 10px;
  flex: 1;
  max-width: 500px;
}

#searchInput {
  flex: 1;
  padding: 12px 15px;
  border: 2px solid #ddd;
  border-radius: 6px;
  font-size: 14px;
  min-width: 300px;
}

#searchInput:focus {
  outline: none;
  border-color: #007bff;
}

/* 操作按鈕容器 */
.operations-container {
  display: flex;
  gap: 10px;
  align-items: center;
}

/* 按鈕基礎樣式 */
.btn-search, .btn-refresh, .btn-new, .btn-delete, .btn-edit, .btn-save, .btn-cancel {
  padding: 10px 20px;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  font-size: 14px;
  font-weight: bold;
  transition: all 0.2s;
  white-space: nowrap;
}

.btn-search { 
  background: #007bff; color: white; border-radius: 0 6px 6px 0;
}
.btn-search:hover { 
  background: #0056b3; 
}

/* 重新整理按鈕 - 灰色 */
.btn-refresh { 
  background: #6c757d; 
  color: white; 
}
.btn-refresh:hover { 
  background: #545b62; 
}

.btn-new { 
  background: #28a745; 
  color: white; 
}
.btn-new:hover { 
  background: #1e7e34; 
}

.btn-delete { 
  background: #dc3545; 
  color: white; 
}
.btn-delete:hover { 
  background: #c82333; 
}

.btn-edit { 
  background: #ffc107; 
  color: #212529; 
}
.btn-edit:hover { 
  background: #e0a800; 
}

.btn-save { 
  background: #28a745; 
  color: white; 
}
.btn-save:hover { 
  background: #1e7e34; 
}

.btn-cancel { 
  background: #6c757d; 
  color: white; 
}
.btn-cancel:hover { 
  background: #545b62; 
}

/* 病患詳細資料區塊 */
.patient-detail-section {
  background: white;
  border: 2px solid #e0e0e0;
  border-radius: 12px;
  margin: 0 auto;
  max-width: 1000px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.1);
}

.detail-header {
  background: #a3d5e4;
  padding: 20px 30px;
  display: flex;
  justify-content: space-between;
  align-items: center;
  border-bottom: 1px solid #ccc;
}

.detail-header h3 {
  margin: 0;
  color: #333;
  font-size: 20px;
  font-weight: bold;
}

.header-actions {
  display: flex;
  gap: 10px;
}

.detail-content {
  padding: 30px;
}

.detail-row {
  display: flex;
  gap: 25px;
  margin-bottom: 25px;
}

.detail-group {
  flex: 1;
  display: flex;
  flex-direction: column;
}

.detail-group.full-width {
  flex: 2;
}

.detail-group label {
  font-weight: bold;
  color: #555;
  margin-bottom: 8px;
  font-size: 14px;
}

.detail-input, .detail-select, .detail-textarea {
  padding: 12px 15px;
  border: 1px solid #ddd;
  border-radius: 6px;
  font-size: 14px;
  transition: border-color 0.3s;
  font-family: inherit;
}

.detail-input:focus, .detail-select:focus, .detail-textarea:focus {
  outline: none;
  border-color: #007bff;
}

.detail-input:disabled, .detail-select:disabled, .detail-textarea:disabled {
  background-color: #f8f9fa;
  color: #6c757d;
  cursor: not-allowed;
}

.detail-textarea {
  resize: vertical;
  min-height: 80px;
}

/* 編輯模式樣式 */
.detail-input.editing, .detail-select.editing, .detail-textarea.editing {
  background-color: #fff;
  border-color: #007bff;
  color: #333;
}

/* 響應式設計 */
@media (max-width: 768px) {
  .patients-container {
    padding: 15px;
  }
  
  .search-operations-row {
    flex-direction: column;
    gap: 15px;
    align-items: stretch;
  }
  
  .search-container {
    max-width: 100%;
  }
  
  .operations-container {
    justify-content: center;
  }
  
  .detail-row {
    flex-direction: column;
    gap: 15px;
  }
  
  .detail-group {
    width: 100%;
  }
  
  .detail-header {
    flex-direction: column;
    gap: 15px;
    align-items: stretch;
  }
  
  .header-actions {
    justify-content: center;
  }
}

/* 小螢幕優化 */
@media (max-width: 480px) {
  .search-container {
    flex-direction: column;
  }
  
  .operations-container {
    flex-direction: column;
    width: 100%;
  }
  
  .operations-container button {
    width: 100%;
  }
}

/* 錯誤訊息樣式 */
.error-message {
    color: #e74c3c;
    font-size: 12px;
    margin-top: 5px;
    display: none;
}

/* 必填欄位標記 */
.required {
    color: #e74c3c;
}

/* 錯誤狀態的輸入框 */
.detail-input.error, .detail-select.error {
    border-color: #e74c3c !important;
}

/* 編輯模式下的錯誤狀態 */
.detail-input.editing.error, .detail-select.editing.error {
    border-color: #e74c3c !important;
    box-shadow: 0 0 0 2px rgba(231, 76, 60, 0.2);
}

patients.html

<!-- 使用者登入後會跳轉至此頁面,用於搜尋、檢視、修改和刪除病患資料 -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Patients</title>
  <link rel="stylesheet" href="style.css">
  <link rel="stylesheet" href="patients.css">
</head>

<body>
  <nav class="navbar">
    <button onclick="window.location.href='index.html'">Login</button>
    <!-- 改為Patients按鈕被設為active -->
    <button class="active">Patients</button>
    <button onclick="window.location.href='records.html'">Records</button>
  </nav>

  <!-- 主內容區 -->
  <div class="patients-container">
    <!-- 頂部提醒框 -->
    <div class="reminder-box">
      <div class="reminder-icon">⚠️</div>
      <div class="reminder-text">
      </div>
    </div>

    <!-- 搜尋和操作區 -->
    <div class="search-operations-row">
      <div class="search-container">
        <input type="text" id="searchInput" placeholder="輸入病患姓名、病歷號或身份證字號進行搜尋...">
        <button id="searchBtn" class="btn-search">搜尋</button>
      </div>
      
      <div class="operations-container">
        <button id="refreshBtn" class="btn-refresh">重新整理</button>
        <button id="newPatientBtn" class="btn-new">新增病患</button>
        <button id="deletePatientBtn" class="btn-delete">刪除病患</button>
      </div>
    </div>

    <!-- 病患詳細資料區塊 -->
    <div class="patient-detail-section">
      <div class="detail-header">
        <h3>病患基本資料</h3>
        <div class="header-actions">
          <button id="editPatientBtn" class="btn-edit" style="display: none;">編輯資料</button>
          <button id="savePatientBtn" class="btn-save" style="display: none;">儲存變更</button>
          <button id="cancelEditBtn" class="btn-cancel" style="display: none;">取消編輯</button>
        </div>
      </div>
      
      <div class="detail-content">
        <div class="detail-row">
          <div class="detail-group">
            <label for="patientName">姓名</label>
            <input type="text" id="patientName" class="detail-input" disabled>
            <div class="error-message" id="nameError"></div>
          </div>
          <div class="detail-group">
            <label for="patientGender">性別</label>
            <select id="patientGender" class="detail-select" disabled>
              <option value="">請選擇</option>
              <option value="M">男</option>
              <option value="F">女</option>
            </select>
            <div class="error-message" id="genderError"></div>
          </div>
          <div class="detail-group">
            <label for="patientBirthday">生日</label>
            <input type="date" id="patientBirthday" class="detail-input" disabled>
            <div class="error-message" id="birthdayError"></div>
          </div>
        </div>

        <div class="detail-row">
          <div class="detail-group">
            <label for="patientIdNumber">身份證字號</label>
            <input type="text" id="patientIdNumber" class="detail-input" disabled maxlength="10">
            <div class="error-message" id="idNumberError"></div>
          </div>
          <div class="detail-group">
            <label for="patientPhone">連絡電話</label>
            <input type="tel" id="patientPhone" class="detail-input" disabled>
            <div class="error-message" id="phoneError"></div>
          </div>
          <div class="detail-group">
            <label for="patientBloodType">血型</label>
            <select id="patientBloodType" class="detail-select" disabled>
              <option value="">請選擇</option>
              <option value="A">A型</option>
              <option value="B">B型</option>
              <option value="O">O型</option>
              <option value="AB">AB型</option>
              <option value="unknown">未知</option>
            </select>
            <div class="error-message" id="bloodTypeError"></div>
          </div>
        </div>

        <div class="detail-row">
          <div class="detail-group full-width">
            <label for="patientAddress">地址</label>
            <input type="text" id="patientAddress" class="detail-input" disabled>
            <div class="error-message" id="addressError"></div>
          </div>
        </div>

        <div class="detail-row">
          <div class="detail-group">
            <label for="patientEmergencyContact">緊急連絡人電話</label>
            <input type="tel" id="patientEmergencyContact" class="detail-input" disabled>
            <div class="error-message" id="emergencyContactError"></div>
          </div>
          <div class="detail-group">
            <label for="patientBadHabits">不良嗜好</label>
            <input type="text" id="patientBadHabits" class="detail-input" disabled placeholder="請輸入不良嗜好...">
            <div class="error-message" id="badHabitsError"></div>
          </div>
          <div class="detail-group">
            <label for="patientFamilyHistory">家族病史</label>
            <input type="text" id="patientFamilyHistory" class="detail-input" disabled placeholder="請輸入家族病史...">
            <div class="error-message" id="familyHistoryError"></div>
          </div>
        </div>

        <div class="detail-row">
          <div class="detail-group">
            <label for="patientMedicalHistory">有無就診記錄</label>
            <select id="patientMedicalHistory" class="detail-select" disabled>
              <option value="">請選擇</option>
              <option value="none">無</option>
              <option value="yes">有</option>
            </select>
            <div class="error-message" id="medicalHistoryError"></div>
          </div>
          <div class="detail-group">
            <label for="patientAllergy">過敏史</label>
            <input type="text" id="patientAllergy" class="detail-input" disabled placeholder="請輸入過敏史...">
            <div class="error-message" id="allergyError"></div>
          </div>
        </div>
      </div>
    </div>
  </div>

  <script src="script.js"></script>
  <script src="patients.js"></script>
</body>
</html>

patients.js

// patients.js - 病患頁面功能(連接真實後端 API)
const API_BASE_URL = 'http://localhost:3001/api';

// 添加全局變數來保存當前病患ID和用戶資訊
let currentPatientId = null;
let currentUser = null;
let currentPatient = null; // 新增:保存完整病患物件
let originalPatientData = null; // 新增:保存原始資料

document.addEventListener('DOMContentLoaded', function() {
    // 檢查登入狀態
    checkLoginStatus();
    // 檢查使用者權限
    checkPermissions();
    // 綁定事件監聽器
    bindEvents();
    // 初始化頁面
    initializePage();
});

// 檢查登入狀態
function checkLoginStatus() {
    const userInfo = localStorage.getItem('userInfo');
    
    if (!userInfo) {
        // 如果未登入,跳轉到登入頁面
        alert('請先登入系統!');
        window.location.href = 'index.html';
        return;
    }
    
    try {
        currentUser = JSON.parse(userInfo);
        console.log('✅ 當前使用者:', currentUser);
        
        // 顯示使用者資訊
        displayUserInfo();
        
    } catch (error) {
        console.error('❌ 解析使用者資訊失敗:', error);
        alert('使用者資訊錯誤,請重新登入!');
        window.location.href = 'index.html';
    }
}

// 顯示使用者資訊在頁面上
function displayUserInfo() {
    // 在提醒框中顯示使用者資訊
    const reminder = document.querySelector('.reminder-text');
    if (reminder && currentUser) {
        reminder.innerHTML = `<strong>${currentUser.roleName}您好!</strong>員工ID: ${currentUser.employeeId}`;
    }
    
    // 也可以在頁面其他地方顯示使用者資訊
    const userInfoElement = document.getElementById('userInfo');
    if (userInfoElement && currentUser) {
        userInfoElement.innerHTML = `
            <div style="display: flex; align-items: center; gap: 15px;">
                <span><strong>${currentUser.roleName}</strong> (ID: ${currentUser.employeeId})</span>
                <button onclick="logout()" style="padding: 5px 10px; background: #dc3545; color: white; border: none; border-radius: 4px; cursor: pointer;">登出</button>
            </div>
        `;
    }
}

// 檢查使用者權限
function checkPermissions() {
    if (!currentUser) {
        alert('無法取得使用者權限,請重新登入!');
        window.location.href = 'index.html';
        return;
    }
    
    console.log('🔑 當前使用者權限:', currentUser.role);
    
    // 隱藏新增按鈕
    document.getElementById('newPatientBtn').style.display = 'none';
    
    // 根據使用者角色設置權限
    if (currentUser.role === 'doctor') {
        document.getElementById('deletePatientBtn').style.display = 'block';
    } else {
        document.getElementById('deletePatientBtn').style.display = 'none';
    }
    
    // 顯示權限提示
    showPermissionMessage();
}

// 顯示權限提示
function showPermissionMessage() {
    const reminder = document.querySelector('.reminder-text');
    if (!reminder || !currentUser) return;
    
    let permissionText = '';
    switch(currentUser.role) {
        case 'doctor':
            permissionText = '您的權限為查詢/修改/刪除資料!';
            break;
        case 'therapist':
            permissionText = '您的權限為查詢/修改資料!';
            break;
        case 'nurse':
        case 'pharmacist':
            permissionText = '您的權限僅為查詢!';
            break;
        default:
            permissionText = '您的權限僅為查詢!';
    }
    
    // 更新提醒框內容,包含權限資訊
    reminder.innerHTML = `
        <strong>${currentUser.roleName}您好!</strong> 
        <span style="margin-left: 10px;">員工ID: ${currentUser.employeeId}</span>
        <br>
        <span style="color: #666; font-size: 14px;">${permissionText}</span>
    `;
}

// 登出功能
function logout() {
    if (confirm('確定要登出系統嗎?')) {
        localStorage.removeItem('userInfo');
        window.location.href = 'index.html';
    }
}

// 綁定事件監聽器
function bindEvents() {
    // 搜尋按鈕
    document.getElementById('searchBtn').addEventListener('click', searchPatient);
    
    // 重新整理按鈕
    document.getElementById('refreshBtn').addEventListener('click', refreshPage);
    
    // 編輯相關按鈕
    document.getElementById('editPatientBtn').addEventListener('click', enableEditing);
    document.getElementById('savePatientBtn').addEventListener('click', savePatient);
    document.getElementById('cancelEditBtn').addEventListener('click', cancelEditing);
    
    // 刪除病患按鈕
    document.getElementById('deletePatientBtn').addEventListener('click', deletePatient);
    
    // 搜尋輸入框按 Enter 鍵搜尋
    document.getElementById('searchInput').addEventListener('keypress', function(e) {
        if (e.key === 'Enter') {
            searchPatient();
        }
    });
}

// 搜尋病患 - 連接真實後端 API
async function searchPatient() {
    const searchTerm = document.getElementById('searchInput').value.trim();
    if (!searchTerm) {
        alert('請輸入搜尋條件!');
        return;
    }
    showLoading(true);
    
    try {
        const apiUrl = `${API_BASE_URL}/patients/search?query=${encodeURIComponent(searchTerm)}`;
        const response = await fetch(apiUrl);
        const data = await response.json();
        
        if (!response.ok) throw new Error(data.error || '搜尋失敗');
        if (!data.patients || data.patients.length === 0) {
            alert('找不到符合條件的病患!');
            refreshPage(); // 找不到資料時也重置頁面
            return;
        }
        
        const patient = data.patients[0];
        
        // 保存完整病患物件
        currentPatient = patient; 
        currentPatientId = patient.PATIENT_ID;
        console.log('💾 保存當前病患:', currentPatient);
        
        fillPatientForm(patient);
        originalPatientData = getFormData();
        
        // 根據權限顯示按鈕
        document.getElementById('editPatientBtn').style.display = (currentUser.role === 'doctor' || currentUser.role === 'therapist') ? 'block' : 'none';
        document.getElementById('deletePatientBtn').style.display = (currentUser.role === 'doctor') ? 'block' : 'none';
        
        alert(`找到病患:${patient.NAME}`);
        
    } catch (error) {
        console.error('❌ 搜尋失敗:', error);
        alert('搜尋失敗:' + error.message);
        refreshPage();
    } finally {
        showLoading(false);
    }
}
// 重新整理頁面
function refreshPage() {
    // 清空搜尋框和表單
    document.getElementById('searchInput').value = '';
    clearPatientForm();
    
    // 隱藏所有按鈕
    document.getElementById('editPatientBtn').style.display = 'none';
    document.getElementById('savePatientBtn').style.display = 'none';
    document.getElementById('cancelEditBtn').style.display = 'none';
    document.getElementById('deletePatientBtn').style.display = 'none';
    document.getElementById('newPatientBtn').style.display = 'none';
    
    // 重置當前病患ID
    currentPatientId = null;
    currentPatient = null;
    // 清空原始資料
    originalPatientData = null;
    
    // 清除錯誤訊息
    clearAllErrors();
    
    alert('頁面已重新整理!');
}

// 初始化頁面
function initializePage() {
    // 確保所有按鈕在初始狀態是隱藏的
    document.getElementById('editPatientBtn').style.display = 'none';
    document.getElementById('savePatientBtn').style.display = 'none';
    document.getElementById('cancelEditBtn').style.display = 'none';
    document.getElementById('deletePatientBtn').style.display = 'none';
    document.getElementById('newPatientBtn').style.display = 'none';
}

// 啟用編輯模式
function enableEditing() {
    // 檢查權限
    if (currentUser.role !== 'doctor' && currentUser.role !== 'therapist') {
        alert('您沒有編輯病患資料的權限!');
        return;
    }
    
    // 確保有病患ID才能編輯
    if (!currentPatientId) {
        alert('請先搜尋要編輯的病患!');
        return;
    }
    
    const inputs = document.querySelectorAll('.detail-input, .detail-select');
    inputs.forEach(input => {
        input.disabled = false;
        input.classList.add('editing');
    });
    
    // 顯示儲存和取消按鈕,隱藏編輯和刪除按鈕
    document.getElementById('editPatientBtn').style.display = 'none';
    document.getElementById('savePatientBtn').style.display = 'block';
    document.getElementById('cancelEditBtn').style.display = 'block';
    document.getElementById('deletePatientBtn').style.display = 'none';
}

// 取得表單資料的函數
function getFormData() {
    return {
        name: document.getElementById('patientName').value,
        gender: document.getElementById('patientGender').value,
        birthday: document.getElementById('patientBirthday').value,
        id_number: document.getElementById('patientIdNumber').value,
        phone: document.getElementById('patientPhone').value,
        blood_type: document.getElementById('patientBloodType').value,
        address: document.getElementById('patientAddress').value,
        emergency_contact: document.getElementById('patientEmergencyContact').value,
        bad_habits: document.getElementById('patientBadHabits').value,
        family_history: document.getElementById('patientFamilyHistory').value,
        allergy: document.getElementById('patientAllergy').value
    };
}

// 取消編輯
function cancelEditing() {
    // 🆕 恢復原始資料
    if (originalPatientData) {
        console.log('🔄 恢復原始資料:', originalPatientData);
        restoreFormData(originalPatientData);
        originalPatientData = null; // 清空保存的資料
    }
    
    const inputs = document.querySelectorAll('.detail-input, .detail-select');
    inputs.forEach(input => {
        input.disabled = true;
        input.classList.remove('editing');
    });
    
    // 根據權限顯示編輯按鈕
    if ((currentUser.role === 'doctor' || currentUser.role === 'therapist') && currentPatientId) {
        document.getElementById('editPatientBtn').style.display = 'block';
    } else {
        document.getElementById('editPatientBtn').style.display = 'none';
    }
    
    document.getElementById('savePatientBtn').style.display = 'none';
    document.getElementById('cancelEditBtn').style.display = 'none';
    
    // 根據權限顯示刪除按鈕
    if (currentUser.role === 'doctor' && currentPatientId) {
        document.getElementById('deletePatientBtn').style.display = 'block';
    } else {
        document.getElementById('deletePatientBtn').style.display = 'none';
    }
    
    // 清除錯誤訊息
    clearAllErrors();
}

// 恢復表單資料的函數
function restoreFormData(data) {
    document.getElementById('patientName').value = data.name || '';
    document.getElementById('patientGender').value = data.gender || '';
    document.getElementById('patientBirthday').value = data.birthday || '';
    document.getElementById('patientIdNumber').value = data.id_number || '';
    document.getElementById('patientPhone').value = data.phone || '';
    document.getElementById('patientBloodType').value = data.blood_type || '';
    document.getElementById('patientAddress').value = data.address || '';
    document.getElementById('patientEmergencyContact').value = data.emergency_contact || '';
    document.getElementById('patientBadHabits').value = data.bad_habits || '';
    document.getElementById('patientFamilyHistory').value = data.family_history || '';
    document.getElementById('patientAllergy').value = data.allergy || '';
}

// 清空病患表單
function clearPatientForm() {
    document.getElementById('patientName').value = '';
    document.getElementById('patientGender').value = '';
    document.getElementById('patientBirthday').value = '';
    document.getElementById('patientIdNumber').value = '';
    document.getElementById('patientPhone').value = '';
    document.getElementById('patientBloodType').value = '';
    document.getElementById('patientAddress').value = '';
    document.getElementById('patientEmergencyContact').value = '';
    document.getElementById('patientBadHabits').value = '';
    document.getElementById('patientFamilyHistory').value = '';
    document.getElementById('patientMedicalHistory').value = '';
    document.getElementById('patientAllergy').value = '';
}

// 填入病患表單 - 處理 null 值
function fillPatientForm(patientData) {
    console.log('📝 填入表單資料:', patientData);
    
    // PATIENTS 表資料
    document.getElementById('patientName').value = patientData.NAME || '';
    document.getElementById('patientGender').value = (patientData.GENDER || '').trim();
    document.getElementById('patientBirthday').value = patientData.BIRTHDAY || '';
    document.getElementById('patientIdNumber').value = patientData.ID_NUMBER || '';
    document.getElementById('patientPhone').value = patientData.PHONE || '';
    document.getElementById('patientBloodType').value = patientData.BLOOD_TYPE || '';
    document.getElementById('patientAddress').value = patientData.ADDRESS || '';
    document.getElementById('patientEmergencyContact').value = patientData.EMERGENCY_CONTACT || '';
    
    // RECORDS 表資料 - 處理 null 值
    document.getElementById('patientBadHabits').value = patientData.BAD_HABITS || '(無資料)';
    document.getElementById('patientFamilyHistory').value = patientData.FAMILY_HISTORY || '(無資料)';
    document.getElementById('patientMedicalHistory').value = patientData.MEDICAL_HISTORY || 'none';
    document.getElementById('patientAllergy').value = patientData.ALLERGY || '(無資料)';
    
    console.log('✅ 表單填入完成');
}

// 儲存病患資料
async function savePatient() {
    // 驗證必填欄位
    if (!validateForm()) {
        return;
    }
    
    // 確保有病患ID
    if (!currentPatientId) {
        alert('請先搜尋要編輯的病患!');
        return;
    }
    
    // 檢查權限
    if (currentUser.role !== 'doctor' && currentUser.role !== 'therapist') {
        alert('您沒有修改病患資料的權限!');
        return;
    }
    
    await updateExistingPatient();
}

// 更新現有病患資料
async function updateExistingPatient() {
    const patientData = getFormData(); // 使用統一的函數取得資料
    
    const patientId = currentPatientId;
    
    console.log('💾 準備更新病患資料,ID:', patientId);
    console.log('📦 更新資料:', patientData);
    
    // 顯示載入中狀態
    showLoading(true);
    
    try {
        // 使用 PUT 方法
        const apiUrl = `${API_BASE_URL}/patients/${patientId}`;
        console.log('🌐 API 網址:', apiUrl);
        
        const response = await fetch(apiUrl, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(patientData)
        });
        
        console.log('📡 回應狀態:', response.status, response.statusText);
        
        const data = await response.json();
        
        if (!response.ok) {
            throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
        }
        
        console.log('✅ 更新成功:', data);
        alert('病患資料已更新成功!');
        
        // 清空原始資料
        originalPatientData = null;
        
        // 回到檢視模式
        cancelEditing();
        
    } catch (error) {
        console.error('❌ 更新失敗:', error);
        alert('更新失敗:' + error.message);
    } finally {
        showLoading(false);
    }
}

// 更新現有病患資料
async function updateExistingPatient() {
    const patientData = {
        name: document.getElementById('patientName').value,
        gender: document.getElementById('patientGender').value,
        birthday: document.getElementById('patientBirthday').value,
        id_number: document.getElementById('patientIdNumber').value,
        phone: document.getElementById('patientPhone').value,
        blood_type: document.getElementById('patientBloodType').value,
        address: document.getElementById('patientAddress').value,
        emergency_contact: document.getElementById('patientEmergencyContact').value,
        bad_habits: document.getElementById('patientBadHabits').value,
        family_history: document.getElementById('patientFamilyHistory').value,
        allergy: document.getElementById('patientAllergy').value
    };
    
    const patientId = currentPatientId;
    
    console.log('💾 準備更新病患資料,ID:', patientId);
    console.log('📦 更新資料:', patientData);
    
    // 顯示載入中狀態
    showLoading(true);
    
    try {
        // 使用 PUT 方法
        const apiUrl = `${API_BASE_URL}/patients/${patientId}`;
        console.log('🌐 API 網址:', apiUrl);
        
        const response = await fetch(apiUrl, {
            method: 'PUT',
            headers: {
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(patientData)
        });
        
        console.log('📡 回應狀態:', response.status, response.statusText);
        
        const data = await response.json();
        
        if (!response.ok) {
            throw new Error(data.error || `HTTP ${response.status}: ${response.statusText}`);
        }
        
        console.log('✅ 更新成功:', data);
        alert('病患資料已更新成功!');
        
        // 回到檢視模式
        cancelEditing();
        
    } catch (error) {
        console.error('❌ 更新失敗:', error);
        alert('更新失敗:' + error.message);
    } finally {
        showLoading(false);
    }
}

// 刪除病患
async function deletePatient() {
    // 步驟 1: 檢查權限和當前狀態
    if (!currentUser || currentUser.role !== 'doctor') {
        alert('您的權限不足,無法刪除病患!');
        return;
    }
  
    if (!currentPatientId || !currentPatient) {
        alert('請先搜尋到要刪除的病患!');
        return;
    }

    // 步驟 2: 顯示一個內容更詳細、更安全的警告確認視窗
    const confirmation = confirm(
        `您確定要刪除病患 「${currentPatient.NAME}」 (ID: ${currentPatientId}) 嗎?\n\n` +
        `🚨 警告:此操作將會永久刪除該病患的所有相關病歷資料 (包含住院、手術、過敏等所有紀錄),且無法復原!`
    );

    if (!confirmation) {
        console.log('使用者取消了刪除操作。');
        return; // 如果使用者按下「取消」,則停止執行
    }
  
    console.log(`準備刪除病患 ID: ${currentPatientId}`);
    showLoading(true);
  
    try {
        // 步驟 3: 發送 DELETE 請求到後端
        const apiUrl = `${API_BASE_URL}/patients/${currentPatientId}`;
        console.log('🌐 發送 DELETE 請求到:', apiUrl);
    
        const response = await fetch(apiUrl, {
            method: 'DELETE',
            headers: {
                'Content-Type': 'application/json',
            }
        });
    
        const data = await response.json();
        console.log('📨 後端回應:', data);
    
        if (!response.ok) {
            throw new Error(data.error || `刪除失敗: ${response.statusText}`);
        }
    
        console.log('✅ 刪除成功:', data);
        alert(`病患 「${currentPatient.NAME}」 及其所有相關資料已成功刪除!`);
    
        // 步驟 4: 刪除成功後,呼叫 refreshPage() 將頁面完全重置
        refreshPage();
    
    } catch (error) {
        console.error('❌ 刪除失敗:', error);
        alert('刪除失敗:' + error.message);
    } finally {
        showLoading(false);
    }
}

// 表單驗證
function validateForm() {
    const name = document.getElementById('patientName').value.trim();
    const gender = document.getElementById('patientGender').value;
    const birthday = document.getElementById('patientBirthday').value;
    const idNumber = document.getElementById('patientIdNumber').value.trim();
    
    if (!name) {
        alert('請填寫病患姓名!');
        document.getElementById('patientName').focus();
        return false;
    }
    
    if (!gender) {
        alert('請選擇性別!');
        document.getElementById('patientGender').focus();
        return false;
    }
    
    if (!birthday) {
        alert('請選擇生日!');
        document.getElementById('patientBirthday').focus();
        return false;
    }
    
    if (!idNumber) {
        alert('請填寫身份證字號!');
        document.getElementById('patientIdNumber').focus();
        return false;
    }
    
    return true;
}

// 顯示/隱藏載入中狀態
function showLoading(show) {
    if (show) {
        let loadingOverlay = document.getElementById('loadingOverlay');
        if (!loadingOverlay) {
            loadingOverlay = document.createElement('div');
            loadingOverlay.id = 'loadingOverlay';
            loadingOverlay.style.cssText = `
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background: rgba(0,0,0,0.5);
                display: flex;
                justify-content: center;
                align-items: center;
                z-index: 9999;
                color: white;
                font-size: 18px;
            `;
            loadingOverlay.innerHTML = `
                <div style="background: white; padding: 30px; border-radius: 10px; color: #333; text-align: center;">
                    <div style="margin-bottom: 15px;">處理中...</div>
                    <div style="width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #007bff; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto;"></div>
                </div>
            `;
            document.body.appendChild(loadingOverlay);
            
            const style = document.createElement('style');
            style.textContent = `
                @keyframes spin {
                    0% { transform: rotate(0deg); }
                    100% { transform: rotate(360deg); }
                }
            `;
            document.head.appendChild(style);
        }
        loadingOverlay.style.display = 'flex';
    } else {
        const loadingOverlay = document.getElementById('loadingOverlay');
        if (loadingOverlay) {
            loadingOverlay.style.display = 'none';
        }
    }
}

// 清除所有錯誤訊息
function clearAllErrors() {
    const errorElements = document.querySelectorAll('.error-message');
    errorElements.forEach(element => {
        element.textContent = '';
        element.style.display = 'none';
    });
    
    const fields = document.querySelectorAll('.detail-input, .detail-select');
    fields.forEach(field => {
        field.style.borderColor = '#ddd';
    });
}

records.html

<!-- 此頁面專門用來檢視特定病患的詳細醫療記錄(手術、住院、過敏、檢驗檢查記錄) -->
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Records</title>
  <link rel="stylesheet" href="style.css">
  <link rel="stylesheet" href="records.css">
</head>
<body>
  <nav class="navbar">
    <button onclick="window.location.href='index.html'">Login</button>
    <button onclick="window.location.href='patients.html'">Patients</button>
    <button classs="active">Records</button>
    <button onclick="window.location.href='schedules.html'">Schedules</button>
    <button onclick="window.location.href='resources.html'">MedicalResourceUsage</button>
  </nav>

  <div class="records-container">
    
    <section class="patient-header-section">

  <div class="patient-unified-card">
    
    <div class="search-area">
      <h4>查詢病患</h4>
      <div class="search-input-group">
        <input type="text" id="patientSearchInput" placeholder="輸入病歷號或姓名...">
        <button id="searchPatientBtn">查詢</button>
      </div>
    </div>

    <hr class="card-divider">

    <div class="info-area">
      <div class="avatar-placeholder"></div>
      <h3 id="patientNameDisplay">請先查詢病患</h3>
      <p id="patientIdDisplay">Patient ID: ---</p>
    </div>

  </div>

</section>

    <section class="records-section">
  <div class="records-grid">
    <div class="record-card">
      <div class="card-header"><h4>手術記錄</h4></div>
      <div class="card-body">
        <div class="form-group">
          <label for="surgeryName">手術名稱</label>
          <textarea id="surgeryName" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="surgeryPart">手術部位</label>
          <textarea id="surgeryPart" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="surgerySuggestion">手術建議</label>
          <textarea id="surgerySuggestion" rows="2" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="surgeryComplications">併發症</label>
          <textarea id="surgeryComplications" rows="2" disabled></textarea>
        </div>
      </div>
    </div>

    <div class="record-card">
      <div class="card-header"><h4>住院記錄</h4></div>
      <div class="card-body grid-2-col">
        <div class="form-group"><label for="hospitalWard">病房名</label><input type="text" id="hospitalWard" disabled></div>
        <div class="form-group"><label for="hospitalBedName">病床名</label><input type="text" id="hospitalBedName" disabled></div>
        <div class="form-group"><label for="hospitalBedNumber">病床號</label><input type="text" id="hospitalBedNumber" disabled></div>
        <div class="form-group"><label for="hospitalStayDays">住院天數</label><input type="text" id="hospitalStayDays" disabled></div>
        <div class="form-group"><label for="admissionDate">住院日期</label><input type="text" id="admissionDate" placeholder="YYYY-MM-DD" disabled></div>
        <div class="form-group"><label for="dischargeDate">出院日期</label><input type="text" id="dischargeDate" placeholder="YYYY-MM-DD" disabled></div>
        <div class="form-group full-width"><label for="admissionReason">住院原因</label><input type="text" id="admissionReason" disabled></div>
        <div class="form-group full-width"><label for="dischargeReason">出院原因</label><input type="text" id="dischargeReason" disabled></div>
      </div>
    </div>

    <div class="record-card">
      <div class="card-header"><h4>過敏記錄</h4></div>
      <div class="card-body">
        <div class="form-group">
          <label for="allergen">過敏原</label>
          <textarea id="allergen" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="allergySymptom">症狀</label>
          <textarea id="allergySymptom" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="allergySeverity">嚴重程度</label>
          <textarea id="allergySeverity" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="allergyNotes">備註</label>
          <textarea id="allergyNotes" rows="2" disabled></textarea>
        </div>
      </div>
    </div>

    <div class="record-card">
      <div class="card-header"><h4>檢驗檢查紀錄</h4></div>
      <div class="card-body">
        <div class="form-group">
          <label for="labTestName">檢驗項目</label>
          <textarea id="labTestName" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="labTestDate">檢驗日期</label>
          <input type="text" id="labTestDate" placeholder="YYYY-MM-DD" disabled>
        </div>
        <div class="form-group">
          <label for="labTestResult">數值/結果</label>
          <textarea id="labTestResult" rows="1" disabled></textarea>
        </div>
        <div class="form-group">
          <label for="labTestSuggestion">醫師建議</label>
          <textarea id="labTestSuggestion" rows="2" disabled></textarea>
        </div>
      </div>
    </div>
  </div>
</section>

  </div>

  <script src="records.js"></script>
</body>
</html>

records.css

/* records.css - 更新按鈕與樣式版本 */

body { font-family: 'Segoe UI', 'Microsoft JhengHei', Arial, sans-serif; margin: 0; background-color: #F4F6F8; }
.navbar { background-color: #a3d5e4; padding: 15px 10px; display: flex; justify-content: center; gap: 20px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); }
.navbar button { background: white; border: none; border-radius: 8px; padding: 10px 25px; color: #333; font-size: 14px; font-weight: bold; cursor: pointer; transition: all 0.2s ease-in-out; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
.navbar button:hover { transform: translateY(-2px); box-shadow: 0 4px 8px rgba(0,0,0,0.15); }
.navbar button.active { background-color: #e9ecef; color: #007bff; box-shadow: inset 0 2px 4px rgba(0,0,0,0.1); }
.records-container { display: flex; flex-direction: column; align-items: center; gap: 30px; padding: 30px; }
.patient-header-section { display: flex; justify-content: center; width: 100%; }
.patient-unified-card { background: white; border-radius: 8px; padding: 24px; box-shadow: 0 2px 10px rgba(0,0,0,0.07); border: 1px solid #EAECEE; width: 100%; max-width: 700px; }
.search-area h4 { margin-top: 0; margin-bottom: 16px; color: #34495E; }
.search-input-group { display: flex; }
.search-input-group input { flex-grow: 1; border: 1px solid #D5D8DC; border-radius: 6px 0 0 6px; padding: 10px; font-size: 14px; }
.search-input-group button { padding: 10px 16px; border: none; background-color: #007bff; color: white; border-radius: 0 6px 6px 0; cursor: pointer; }
.card-divider { border: none; border-top: 1px solid #EAECEE; margin: 24px 0; }
.info-area { text-align: center; }
.avatar-placeholder { width: 80px; height: 80px; border-radius: 50%; background-color: #EAECEE; margin: 0 auto 15px; }
#patientNameDisplay { margin: 10px 0 5px; color: #2C3E50; font-size: 1.1em; font-weight: bold; }
#patientIdDisplay { margin: 0; color: #808B96; font-size: 14px; }
.records-section { width: 100%; max-width: 1800px; }
.records-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 16px; width: 100%; }
.record-card { background: white; border-radius: 8px; box-shadow: 0 2px 10px rgba(0,0,0,0.07); border: 1px solid #EAECEE; display: flex; flex-direction: column; min-width: 0; }
.card-header { background: #a3d5e4; padding: 12px 16px; border-bottom: 1px solid #90C4D5; border-radius: 8px 8px 0 0; }
.card-header h4 { margin: 0; color: #333; font-size: 0.95em; font-weight: bold; white-space: nowrap; }
.card-body { padding: 16px; display: flex; flex-direction: column; gap: 12px; }
.card-body.grid-2-col { display: grid; grid-template-columns: repeat(2, minmax(

上一篇
Day27:前端設計 - 病患病歷頁面
下一篇
Day29:前端設計 - 統整全部程式碼、實際Demo影片
系列文
基於 Oracle 資料庫的醫院電子病歷系統設計與建置30
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言